<?php

namespace apiend\models\forms;

use Yii;
use yii\db\Exception;
use common\models\app\AppToken;
use common\models\app\AppDevice;
use common\models\app\AppClient;
use common\models\app\AppVersion;

/**
 * APP接口访问授权接口
 * Authorization
 * 
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class AuthorizationForm extends \common\base\Model {

    /**
     * @var string 授权应用ID
     */
    public $appid;

    /**
     * @var string 时间戳
     */
    public $timestamp;

    /**
     * @var string 设备唯一编号
     */
    public $sn;

    /**
     * @var string 客户端操作系统
     */
    public $os;

    /**
     * @var string 设备机型
     */
    public $model;

    /**
     * @var integer 客户端Build版本号
     */
    public $build;

    /**
     * @var string 随机字符串
     */
    public $nonce;

    /**
     * @var string 签名字符串
     */
    public $sign;

    /**
     * @var string 安装类型
     */
    public $install = 0;

    /**
     * @var object 客户端
     */
    private $_client = null;

    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            [['appid', 'sn', 'os', 'build', 'nonce', 'sign', 'timestamp'], 'required'],
            [['build', 'timestamp', 'install'], 'integer'],
            [['sn', 'model'], 'string', 'max' => 64],
            [['os', 'nonce'], 'string', 'max' => 32],
            ['appid', 'validateAppId'],
            ['build', 'validateBuild'],
            ['sign', 'validateSignature'],
        ];
    }

    /**
     * 验证APPID是否为授权应用
     * @param string $attribute `appid`
     */
    public function validateAppId($attribute) {
        if ($this->hasErrors()) {
            return;
        }
        if (!$this->getClient()) {
            $this->addError($attribute, '未授权的应用ID');
        }
    }

    /**
     * 验证Build版本号是否正确
     * @param string $attribute `build`
     */
    public function validateBuild($attribute) {
        if ($this->hasErrors()) {
            return;
        }
        $version = AppVersion::find()->where([
            'build' => $this->build,
            'os' => $this->os,
        ])
        ->andWhere(['>', 'status', AppVersion::STATUS_DEFAULT])
        ->exists();
        if (!$version) {
            $this->addError($attribute, '请求的版本已不提供支持');
        }
    }

    /**
     * 校验请求的签名
     * @param string $attribute `sign`
     */
    public function validateSignature($attribute) {
        if ($this->hasErrors()) {
            return;
        }
        $validated = Yii::$app->security->validateSignature($this->attributes, $this->getClient()->secret);
        if ($validated !== true) {
            $tips = '签名校验失败';
            if (YII_DEBUG) {
                $tips .= $validated;
            }
            $this->addError($attribute, $tips);
        }
    }

    /**
     * 获取APP授权应用客户端
     * @return \common\models\app\AppClient|null
     */
    protected function getClient() {
        if ($this->_client === null) {
            $this->_client = AppClient::findByAppId($this->appid);
        }
        return $this->_client;
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels() {
        return [
            'appid' => 'AppID',
            'sn' => '设备号',
            'model' => '设备型号',
            'os' => '系统',
            'build' => 'Build',
            'install' => '安装类型',
            'nonce' => '随机串',
            'timestamp' => '时间戳',
            'sign' => '签名',
        ];
    }

    /**
     * 认证授权操作
     * @inheritdoc
     */
    public function authorize() {
        if (!$this->validate()) {
            return $this->validateError('请求数据校验失败');
        }
        $token = $this->executeTransaction(__FUNCTION__);
        $data = [
            'access_token' => $token->access_token,
            'expired_at' => $token->expired_at,
        ];
        if (YII_DEBUG) {
            $data['_debug'] = [
                'encrypt_token' => Yii::$app->security->encryptDataByKey($token->access_token)
            ];
        }
        return $data;
    }

    /**
     * 数据保存
     * @inheritdoc
     */
    protected function transactAuthorize() {
        $device = AppDevice::findOrNew([
            'sn' => $this->sn,
            'os' => $this->os,
        ]);
        $device->attributes = [
            'build' => $this->build,
            'model' => $this->model,
        ];
        if (!$device->save()) {
            throw new Exception('设备添加失败', $device->errorMsg);
        }
        $token = AppToken::findOrNew([
            'client_id' => $this->client->id,
            'device_id' => $device->id,
        ]);
        if ($this->install) {
            $token->user_id = 0;
        }
        $token->generateAccessToken();
        if (!$token->save()) {
            throw new Exception('Token生成失败', $token->errorMsg);
        }
        return $token;
    }

}
